<?php
namespace app\common\model;

use think\facade\Request;

/**
 * 查询对象
 * @property Model $model 当前模型对象
 */
class Query extends \think\db\Query
{
    /**
     * 自动排序
     * @return $this
     */
    public function autoOrder($scope = ''): static
    {
        if (false === $this->model->orderAllow) return $this;

        $orderParam = Request::param($this->model->orderInputKey, []);
        if (is_string($orderParam)) $orderParam = [$orderParam];

        $list = [];
        foreach ($orderParam as $order) {
            [$field, $sort] = explode(',', $order);

            if (true !== $this->model->orderAllow && !in_array($field, $this->model->orderAllow)) continue;

            $list[$field] = $sort;
        }

        if (empty($list)) {
            if ('' !== $scope) $this->scope($scope);
        } else {
            $this->order($list);
        }

        return $this;
    }

    /**
     * 自动搜索
     * @param array        $field 要搜索的字段
     * @param array|string $data  数据或数据获取方法
     * @return $this
     */
    public function autoSearch(array $field = [], array|string $data = 'param'): static
    {
        $allow = [];

        // 获取已定义搜索器，添加对应字段到允许搜索范围里
        foreach(get_class_methods($this->model) as $method) {
            if (preg_match('@^search([a-zA-Z0-9_]+)Attr$@', $method, $matches)) {
                $allow[] = strtolower(trim(preg_replace('/[A-Z]/', '_\\0', $matches[1]), '_'));
            }
        }

        // 获取搜素配置
        $searchOptions = $this->model->getSearchOptions();

        // 获取已定义自动搜索条件，添加对应字段到允许搜索范围里
        $allow = array_merge($allow, array_keys($searchOptions));

        if (empty($field)) {
            $field = $allow;
        } else {
            $field = array_intersect($allow, $field);
        }

        $data = is_array($data) ? $data: array_filter(
            Request::only($field, $data, ''),
            function($value) {
                return is_array($value) ? (count($value) > 0) : ('' != trim($value));
            }
        );

        $field = $map = [];
        $alias = $this->getAlias();
        $alias = null === $alias ? '' : $alias . '.';
        foreach (array_keys($data) as $key) {
            if (array_key_exists($key, $searchOptions)) {
                $option = $searchOptions[$key];
                switch ($option[0]) {
                    case 'like':
                        $map[] = [$alias . $key, $option[0], sprintf($option[1] ?? '%s', $data[$key])];
                        break;
                    case 'in':
                        if (is_array($data[$key])) {
                            $map[] = [$alias . $key, $option[0], $data[$key]];
                        } else {
                            $map[] = [$alias . $key, '=', $data[$key]];
                        }
                        break;
                    case 'between':
                        // 如果是字符串，则分割成数组
                        if (is_string($data[$key])) {
                            if (str_contains($data[$key], '<>')) {
                                $option[0] = 'NOT BETWEEN';
                                $option[1] = '<>';
                                $data[$key] = array_map('trim', explode( '<>', $data[$key], 2));
                            } else {
                                $option[1] = sprintf('@%s@', $option[1] ?? '(\s+-\s+|\s*[~,]\s*)');
                                $data[$key] = array_map('trim', preg_split($option[1], $data[$key], 2));
                            }
                        }

                        if (in_array($data[$key][0], ['>', '>=', '<', '<='])) {
                            // 比较运算
                            $map[] = [$alias . $key, $data[$key][0], $data[$key][1]];
                        } else {
                            // 区间运算
                            $map[] = [$alias . $key, $option[0], $data[$key]];
                        }
                        break;
                    case 'timestamp':
                    case 'datetime':
                    case 'date':
                    case 'year-month':
                        // 如果是字符串，则分割成数组
                        if (is_string($data[$key])) {
                            $data[$key] = array_map('trim', preg_split('(\s+-\s+|\s*[~,]\s*)', $data[$key], 2));
                        }

                        if (in_array($data[$key][0], ['>', '>=', '<', '<='])) {
                            // 比较运算
                            $map[] = [$alias . $key, $data[$key][0], \DateTools::improved($data[$key][1])];
                        } else {
                            // 区间运算
                            $map[] = [$alias . $key, 'between', [
                                \DateTools::improved($data[$key][0]),
                                \DateTools::improved($data[$key][1] ?? $data[$key][0], false)]
                            ];
                        }
                        break;
                    default:
                        $map[] = [$alias . $key, $option[0], $data[$key]];
                }
                unset($data[$key]);
            } else {
                $field[] = $key;
            }
        }

        return $this->where($map)->withSearch($field, $data);
    }

    /**
     * 获取数据表别名
     * @param string|null $table 表名
     * @return string|null
     */
    public function getAlias(?string $table = null): ?string
    {
        if (null === $table) $table = $this->getTable();
        return $this->options['alias'][$table] ?? null;
    }
}